# 2021-8-28 8:17 修正对填空题是否填写的判断 by grx
import math

from rest_framework.decorators import api_view
from questionnaire.models import *
from rest_framework.response import Response
from rest_framework import status
from django.http import FileResponse
from django.db.models import Avg, Max, Min, Count, Sum
from questionnaire.views_export import *


# 填写问卷总人数统计
def totalQuestionnaire(wid):
    res = Submit.objects.filter(wjId=wid).filter(is_submitted=True).aggregate(c=Count("id"))
    # 计算成功返回对应数字，失败则返回’-‘
    if res:
        return res['c']
    else:
        return '-'


# 填写问卷平均时间
def avgTimeToQuestionnaire(wid):
    res = Submit.objects.filter(wjId=wid).filter(is_submitted=True).aggregate(a=Avg("useTime"))
    # 计算成功则返回对应数值字符串，失败则返回‘-时-分-秒’
    if res and res['a']:
        t = math.floor(res['a'])
        s = str(int(t % 60))
        m = str(int((t // 60) % 60))
        h = str(int(t // 3600))
    else:
        s, m, h = '-', '-', '-'
    return h + '时' + m + '分' + s + '秒'


# 直方图统计
# 计算各组样本数
def countSample(ls, inf, sup):
    s = 0
    ls.sort()
    for item in ls:
        if item < inf:
            continue
        elif item >= sup:
            break
        s += 1
    return s


def timeHistogram(wid):
    res = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('useTime', flat=True))
    res.sort()
    if len(res) == 0:
        return '-'
    # 获取上下限，并计算出极差
    r = res[-1] - res[0]
    k = math.floor(math.sqrt(r)) + 1
    d = r // k
    dict_his = {}
    for i in range(k):
        if i == k - 1:
            dict_his[str((res[0] + i * d, res[0] + (i + 1) * d))] = countSample(res, res[0] + i * d, res[-1] + 1)
        else:
            dict_his[str((res[0] + i * d, res[0] + (i + 1) * d))] = countSample(res, res[0] + i * d,
                                                                                res[0] + (i + 1) * d)
    return dict_his


# 计算某道题的填写率
def getFillingRate(q, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    # 找出所有回答的数量
    total = Submit.objects.filter(wjId=q.wjId).filter(is_submitted=True).count()
    # 填写率有意义时，计算数值并返回;无意义时，返回’-‘(页面显示’-%‘)
    if total != 0:
        if q.type == 'fill':
            ans = Answer.objects.filter(questionId=q.id).filter(submitId__in=ls_submitted).exclude(
                answerText="").exclude(answerText=None).count()
        else:
            ans = len(Answer.objects.filter(questionId=q.id).filter(submitId__in=ls_submitted).exclude(
                answer=None).values_list('submitId', flat=True).distinct())
        return "%.2f" % ((ans * 100) / total)
    else:
        return '-'


# 选择题统计
# 统计单个问题各个选项的选择人数分布
def optionCount(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    # 找出所有回答
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted)
    # 找出全部的选项,注意选项可能会重复
    opt = Options.objects.filter(questionId=qid)
    dict_opt = {}
    # 初始化, 键：值 = 选项id:[选项内容, 选择人数, 选择比例]
    for item in opt:
        # 保证title不重复
        if [item.title, 0, ''] not in dict_opt.values():
            dict_opt[item.id] = [item.title, 0, '']
    for a in ans:
        # 对异常的选择结果进行忽略处理
        if a and (a.answer in dict_opt.keys()):
            # a.answer即选项id
            dict_opt[a.answer][1] += 1
    # 计算选择比例
    # 计算填写该题的总人数,若填写人数为0则选择比例为’-%‘
    total = len(list(ans.exclude(answer=None).values_list('submitId', flat=True).distinct()))
    for v in dict_opt.values():
        if total != 0:
            v[2] = ("%.2f" % (v[1] * 100 / total)) + '%'
        else:
            v[2] = '-%'
    return dict_opt


# 统计单个题目的平均完成时间

# 填空题统计
# 总字数统计, 传入某一道题的id
def totalWordCount(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    total = 0
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted).values_list('answerText', flat=True)
    for text in ans:
        total += len(text)
    return total


# 平均数统计
def avgWordCount(qid, ls_submitted):
    total_word = totalWordCount(qid, ls_submitted)
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted).count()
    if ans != 0:
        return "%.2f" % (total_word / ans)
    else:
        return '-'


# 中位数统计
def medianWordCount(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted).values_list('answerText', flat=True)
    word_count = []
    for text in ans:
        word_count.append(len(text))
    word_count.sort()

    if len(word_count) != 0:
        if len(word_count) % 2 != 0:
            return word_count[len(word_count) // 2]
        return (word_count[len(word_count) // 2] + word_count[len(word_count) // 2 - 1]) / 2
    return '-'


# 直方图数据统计
# 依据文本字数上限和最少字数（一般为1、0）计算出极差，开方计算出分组数量k，计算组距，分组（建立k个列表？），计算各组样本数
# 传给前端【区间划分】和【对应样本数】
def wordCountHistogram(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    # 获得该题全部数据（字数）
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted).values_list('answerText', flat=True)
    word_count = []
    for text in ans:
        word_count.append(len(text))
    word_count.sort()
    if len(word_count) == 0:
        return {'0': 0}
    # 获取文本字数上下限，并计算出极差
    r = word_count[-1] - word_count[0]
    k = math.floor(math.sqrt(r)) + 1
    d = r // k
    print(word_count)
    print(r, k, d)
    dict_his = {}
    for i in range(k):
        if i == k - 1:
            dict_his[str((word_count[0] + i * d, word_count[0] + (i + 1) * d))] = countSample(word_count,
                                                                                              word_count[0] + i * d,
                                                                                              word_count[-1] + 1)
        else:
            dict_his[str((word_count[0] + i * d, word_count[0] + (i + 1) * d))] = countSample(word_count,
                                                                                              word_count[0] + i * d,
                                                                                              word_count[0] + (
                                                                                                          i + 1) * d)
    return dict_his


# 便于前端的函数
def getResultOfFill(q, ls_submitted):
    rsl = [{'filling_rate': str(getFillingRate(q, ls_submitted)) + '%',
            'total_word_count': totalWordCount(q.id, ls_submitted),
            'avg_word_count': avgWordCount(q.id, ls_submitted),
            'median_word_count': medianWordCount(q.id, ls_submitted)}]
    return rsl


def gradeCount(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    # 找出所有回答
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted)
    # 找出评分题
    grade = GradeQuestionAttribute.objects.filter(questionId=qid).first()
    dict_grade = {}
    # 初始化, 键：值 = 选项id:[选项内容, 选择人数, 比例]  选项id为0——(range-1)
    for i in range(0, grade.range):
        dict_grade[i] = [grade.start + i, 0, '']
    for a in ans:
        # 对异常的选择结果进行忽略处理
        if a and ((a.answer - grade.start) in dict_grade.keys()):
            dict_grade[a.answer - grade.start][1] += 1
    # 计算选择比例
    # 计算填写该题的总人数
    total = len(list(ans.exclude(answer=None).values_list('submitId', flat=True).distinct()))
    for v in dict_grade.values():
        if total != 0:
            v[2] = v[2] = ("%.2f" % (v[1] * 100 / total)) + '%'
        else:
            v[2] = '-%'
    return dict_grade


# 统计评分中位数
def medianGrade(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted)
    grade_count = []
    for a in ans:
        grade_count.append(a.answer)
    grade_count.sort()

    if len(grade_count) != 0:
        if len(grade_count) % 2 == 0:
            return (grade_count[len(grade_count) // 2] + grade_count[len(grade_count) // 2 - 1]) / 2
        return grade_count[len(grade_count) // 2]
    return '-'


def avgGradeCount(qid, ls_submitted):
    # # 找出所有有效提交的submitId
    # ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
    ans = Answer.objects.filter(questionId=qid).filter(submitId__in=ls_submitted)
    grade_count = []
    for a in ans:
        grade_count.append(a.answer)
    total_grade = sum(grade_count)

    if len(grade_count) != 0:
        return "%.2f" % (total_grade / len(grade_count))
    return '-'


# optionCount/gradeCount的另一种格式，为了方便前端
# [{'option': 选项标题, 'count': 人数, 'percent': 占比}, {...}, ...]
def getResult(q, ls_submitted):
    rsl = []
    dic = {}
    if q.type in ['single', 'multi']:
        dic = optionCount(q.id, ls_submitted)
    elif q.type == 'grade':
        dic = gradeCount(q.id, ls_submitted)
    for d in dic.values():
        tmp_d = {'option': d[0], 'count': d[1], 'percent': d[2]}
        rsl.append(tmp_d)
    return rsl


@api_view(['POST', ])
# 整合数据
def getStatisticalData(request):
    wid = request.data.get('qid')
    if wid:
        # 找出所有有效提交的submitId
        ls_submitted = list(Submit.objects.filter(wjId=wid).filter(is_submitted=True).values_list('id', flat=True))
        # print(ls_submitted)
        dict_stat = {}
        # 问卷整体数据
        # 问卷总份数
        dict_stat['total'] = totalQuestionnaire(wid)
        # 填写问卷平均时间
        dict_stat['avg_time'] = avgTimeToQuestionnaire(wid)
        # 填写问卷时间分布直方图数据
        dict_stat['time_histogram'] = timeHistogram(wid)
        # 判断数据库中有无对应问卷id的有效提交
        if dict_stat['time_histogram'] != '-':
            # 获取本问卷的所有题目的qid
            questions = Question.objects.filter(wjId=wid)
            # 该问卷是否存在对应的问题
            if questions:
                dict_stat['question_infos'] = []
                # [{问题id：{统计数据项名称：数据内容, ...}}, {...}, ...]
                for q in questions:
                    # 本题填写率
                    dict_q = {'id': q.id, 'title': q.title, 'type': q.type,
                              'filling_rate': str(getFillingRate(q, ls_submitted)) + '%'}
                    # 针对不同题型进行不同分析
                    if q.type in ['single', 'multi']:
                        dict_q['result'] = getResult(q, ls_submitted)
                        dict_q['option_count'] = optionCount(q.id, ls_submitted)
                    elif q.type == 'fill':
                        # 总字数统计
                        dict_q['total_word_count'] = totalWordCount(q.id, ls_submitted)
                        # 平均字数统计
                        dict_q['avg_word_count'] = avgWordCount(q.id, ls_submitted)
                        # 中位数统计
                        dict_q['median_word_count'] = medianWordCount(q.id, ls_submitted)
                        # 'result'
                        dict_q['result'] = getResultOfFill(q, ls_submitted)
                        # 直方图数据
                        dict_q['word_count_histogram'] = wordCountHistogram(q.id, ls_submitted)
                        # 判断直方图数据是否存在
                        # if dict_q['word_count_histogram'] == '-':
                        #     dict_stat['question_infos'].append(dict_q)
                        #     response = {
                        #         'code': 4,
                        #         'msg': '数据库中改题目无有效提交',
                        #         'statistics': dict_stat
                        #     }
                        #     return Response(response, status=status.HTTP_200_OK)
                    elif q.type == 'grade':
                        # 分数项分布
                        dict_q['result'] = getResult(q, ls_submitted)
                        dict_q['option_count'] = gradeCount(q.id, ls_submitted)
                        # 统计评分平均数
                        dict_q['avg_grade'] = avgGradeCount(q.id, ls_submitted)
                        # 统计评分中位数
                        dict_q['median_grade'] = medianGrade(q.id, ls_submitted)
                    # 加入总字典中
                    dict_stat['question_infos'].append(dict_q)
                response = {
                    'code': 0,
                    'msg': 'success',
                    'statistics': dict_stat
                }
            else:
                response = {
                    'code': 3,
                    'msg': '该问卷id所对应的问卷下不存在问题',
                    'statistics': dict_stat
                }
        else:
            response = {
                'code': 2,
                'msg': '数据库中无对应问卷id的有效提交',
                'statistics': dict_stat
            }
    else:
        response = {
            'code': 1,
            'msg': '获取问卷id失败'
        }
    return Response(response, status=status.HTTP_200_OK)


